package org.mika.asm;

import java.lang.reflect.Method;

import net.sf.cglib.asm.ClassWriter;
import net.sf.cglib.asm.Type;
import net.sf.cglib.core.ClassEmitter;
import net.sf.cglib.core.ClassGenerator;
import net.sf.cglib.core.CodeEmitter;
import net.sf.cglib.core.Constants;
import net.sf.cglib.core.EmitUtils;
import net.sf.cglib.core.GeneratorStrategy;
import net.sf.cglib.core.ReflectUtils;

class MethodsImplGenerator<T,I> implements GeneratorStrategy {
	private Class<? extends T> opener;
	private Class<? extends I> interf;
	private Class<T> original;

	public MethodsImplGenerator(Class<T> original, Class<? extends T> o, Class<I> i) {
		opener = o; 
		interf = i; 
		this.original = original;
	}

	@Override
	public byte[] generate(ClassGenerator cg) throws Exception {
		ClassWriter  writer  = new ClassWriter(true);
		ClassEmitter ce = new ClassEmitter(writer);
		
		ASMUtils.generateImplementatorHeader(ce, opener, interf);
		String fieldName = ASMUtils.generateWrapped(ce, original);
				
		for(Method m : interf.getDeclaredMethods()) {
			CodeEmitter method = EmitUtils.begin_method(ce, ReflectUtils.getMethodInfo(m), Constants.ACC_PUBLIC);			
			
			method.load_this();
			method.getfield( fieldName );
			method.push( MethodUtils.getMethodSignature(m) );
			
			ASMUtils.copyArgs(method, m);
			
			method.invoke_static(
				Type.getType( opener ),
				ReflectUtils.getSignature(
					opener.getMethod(MethodUtils.INVOKE_METHOD, new Class[]{
						Object.class, String.class, Object[].class 
					})
				)
			);
			
			ASMUtils.returnFromStack(m.getReturnType(), method);
		}
		
		ce.end_class();		
		return writer.toByteArray();
	}
}
